#include "ConnectPool.h"
#include <json/json.h>
#include <fstream>
#include <thread>
using namespace Json;

ConnectPool * ConnectPool::get_connect_pool()
{
	static ConnectPool pool;
	return &pool;
}

ConnectPool::ConnectPool()
{
	//	加载配置文件
	if (!perse_json_file()) 
		return;

	//	初始化连接池连接
	for (int i = 0; i < m_min_size; i++)
		add_connection();

	//	创建负责创建连接的线程
	thread producer(&ConnectPool::produce_connection,this);
	//	创建负责销毁连接的线程
	thread recycler(&ConnectPool::recycle_connection, this);
	producer.detach();
	recycler.detach();
}

//	添加连接函数
void ConnectPool::add_connection()
{
	MysqlOperation* conn = new MysqlOperation;
	conn->connect(m_user, m_passwd, m_db_name, m_ip, m_port);
	conn->refresh_alive_time();
	m_connect_q.push(conn);
}

//	利用jsoncpp对json文件解析
bool ConnectPool::perse_json_file()
{
	ifstream ifs("config.json");
	Reader rd;
	Value root;
	rd.parse(ifs,root);
	//	判断存储到root里面的是不是json对象
	if (root.isObject())
	{
		// 获取每个字段的值
		m_ip = root["ip"].asString();
		m_user = root["user"].asString();
		m_passwd = root["password"].asString();
		m_db_name = root["db_name"].asString();
		m_port = root["port"].asInt();
		m_max_size = root["max_size"].asInt();
		m_min_size = root["min_size"].asInt();
		m_timeout = root["timeout"].asInt();
		m_max_idle_time = root["max_idle_time"].asInt();

		//ifs.close();
		return true;
	}

	//ifs.close();
	return false;
}

//	创建连接管理线程的任务函数
void ConnectPool::produce_connection()
{
	while (true) {
		//	判断当前连接数是否够用
		unique_lock<mutex> locker(m_mx_q);		//	unique_lock 析构后 m_mx_q会自动解锁
		while (m_connect_q.size() >= m_min_size) 
			m_cond.wait(locker);

		add_connection();

		//	添加了一个新连接，调用对应的条件唤醒函数唤醒消费者
		m_cond.notify_all();
	}
}

//	销毁连接管理线程的任务函数
void ConnectPool::recycle_connection()
{
	while (true) {
		this_thread::sleep_for(chrono::milliseconds(500));
		//	因为队列的特性，越靠近队头的元素空闲时间越长
		lock_guard<mutex> locker(m_mx_q);
		while (m_connect_q.size() > m_min_size)
		{
			//	判断线程的空闲时间是否大于预定的最大空闲时间
			MysqlOperation* conn = m_connect_q.front();
			if (conn->get_alive_time() >= m_max_idle_time)
			{
				m_connect_q.pop();
				delete conn;
			}
			else
				break;
		}
	}
}
//	给外部提供数据库连接,用户用完后使用共享内存指针来回收
shared_ptr<MysqlOperation> ConnectPool::get_connection()
{
	//	判断连接池里面的连接是否为空
	unique_lock<mutex> locker(m_mx_q);
	while(m_connect_q.empty()) 
	{
		//	判断是否超过超时时间还是拿不到
		if (cv_status::timeout == m_cond.wait_for(locker, chrono::milliseconds(m_timeout)))
		{
			//	判断此时的队列是否还是为空
			if (m_connect_q.empty())
			{
				//return nullptr;
				continue;
			}
		}
	}

	//	不需要把共享内存指针所指的内存销毁掉，所以需要自定义一个规则,这里使用lambda表达式
	shared_ptr<MysqlOperation> conn_ptr(m_connect_q.front(), [this](MysqlOperation* conn) {
		//	将内存放回连接池里面的队列中
		lock_guard<mutex> locker(m_mx_q);	//	第二种智能锁方式
		conn->refresh_alive_time();
		m_connect_q.push(conn);
		m_cond.notify_all();

	});
	m_connect_q.pop();

	//	消费者拿走了连接后，需要唤醒生产者
	m_cond.notify_all();
	return conn_ptr;
}

ConnectPool::~ConnectPool()
{
	//判断队列是否为空
	while (!m_connect_q.empty()) {
		MysqlOperation* conn = m_connect_q.front();
		m_connect_q.pop();
		delete conn;
	}
}